注意:所有文章除特别说明外,转载请注明出处.
第12章 HandlerMapping
[TOC]
HandlerMapping的作用是通过request查找Handler和Interceptors。HandlerMapping包括继承AbstractUrlHandlerMapping和继承AbstractHandlerMapping,这两支都继承抽象类AbstractHandlerMapping。
12.1 AbstractHandlerMapping
AbstractHandlerMapping是HandlerMapping抽象实现。其采用模板模式设计了HandlerMapping实现的整体结构。子类通过模板方法提供初始值或者具体算法即可。这种模式是:首先使用一个抽象实现采用模板模式进行整体设计,然后在子类通过实现模板方法具体实现业务。
HandlerMapping的作用是是通过request查找Handler和Interceptors。获取Handler的过程通过模板方法getHandlerInternal()交给子类。AbstractHandlerMapping中保存了所有的配置Interceptor,在获取Handler后会自己根据从request中获取的lookupPath将相应的Interceptor装配。子类也可以如此。
12.1.1 创建AbstractHandlerMapping
AbstractHandlerMapping继承WebApplicationObjectSupport,初始化时会自动调用模板方法initApplicationContext()方法。AbstractHandlerMapping创建是在initApplicationContext()方法中。
package org.springframework.web.servlet.handler;
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered {
// ...
}
protected void initApplicationContext() throws BeansException {
//该方法是模板方法,给子类提供一个添加或修改Interceptors的入口
extendInterceptors(this.interceptors);
//将SpringMVC容器和父容器中所有的MappedInterceptor类型的Bean添加到mappedInterceptors属性
detectMappedInterceptors(this.mappedInterceptors);
//该方法的作用是初始化Interceptor
initInterceptors();
}
//提供给子类扩展拦截器(没有使用)
protected void extendInterceptors(List<Object> interceptors) {
}
//扫描应用下的MappedInterceptor,并添加到mappedInterceptors
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) {
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(),MappedInterceptor.class, true, false).values());
}
//归集MappedInterceptor,并适配HandlerInterceptor和WebRequestInterceptor
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
for (int i = 0; i < this.interceptors.size(); i++) {
Object interceptor = this.interceptors.get(i);
if (interceptor == null) {
throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
}
if (interceptor instanceof MappedInterceptor) {
mappedInterceptors.add((MappedInterceptor) interceptor);
}
else {
adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
}
AbstractHandlerMapping的属性:
// order赋了最大值,优先级是最小的
private int order = Integer.MAX_VALUE;
// 默认的Handler,这边使用的Obejct,子类实现的时候,使用HandlerMethod,HandlerExecutionChain等
private Object defaultHandler;
// url计算的辅助类
private UrlPathHelper urlPathHelper = new UrlPathHelper();
// 基于ant进行path匹配,解决如/books/{id}场景
private PathMatcher pathMatcher = new AntPathMatcher();
// 拦截器配置:两种配置方式 interceptors只用于配置
// 1 HandlerMapping属性设置;
// 2 extendInterceptors设置
private final List<Object> interceptors = new ArrayList<Object>();
// 从interceptors中解析得到,直接添加给全部handler
private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<HandlerInterceptor>();
// 使用前需要跟url进行匹配,匹配通过才会使用。匹配成功后将其添加到getHandler的返回值HandlerExecutionChain中。
private final List<MappedInterceptor> mappedInterceptors = new ArrayList<MappedInterceptor>();
提示:WebApplicationObjectSupport用于提供上下文ApplicationContext和ServletContext。AbstractHandlerMapping的创建就是上面三个Interceptor的初始化。
12.1.2 AbstractHandlerMapping的使用
HandlerMapping通过getHandler()方法获取处理器Handler和拦截器Interceptors。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
//留给子类具体实现,子类主要做的事情
Object handler = getHandlerInternal(request);
//没有获取到则使用默认的Handler
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
return getHandlerExecutionChain(handler, request);
}
//这里预留getHandlerInternal(HttpServletRequest request)方法给子类实现
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
然后封装拦截器到HandlerExecutionChain(),该方法用于添加拦截器。
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
//1.首先使用handler创建HandlerExecutionChain类型变量
HandlerExecutionChain chain =
(handler instanceof HandlerExecutionChain) ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler);
//adaptedInterceptors直接添加
chain.addInterceptors(getAdaptedInterceptors());
//mappedInterceptors需要根据url匹配通过后添加
String lookupPath = urlPathHelper.getLookupPathForRequest(request);
for (MappedInterceptor mappedInterceptor : mappedInterceptors) {
if (mappedInterceptor.matches(lookupPath, pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
return chain;
}
12.2 AbstractUrlHandlerMapping
从名字可以看出是通过url进行匹配的。大致原理是将url与对应的handler保存到一个Map中,在getHandlerInternal()方法中使用url从Map中获取Handler,AbstractUrlHandlerMapping中实现了具体用url从Map中获取Handler的过程。
@Override
protected Object getHandlerInternal(HttpServletRequest request) throws Exception {
// 根据request获取url
String lookupPath = getUrlPathHelper().getLookupPathForRequest(request);
// 根据url查找handler
Object handler = lookupHandler(lookupPath, request);
if (handler == null) {
// 如果没有匹配到handler需要查找默认的,下面需要将PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE缓存到request
//临时变量,保存找到的原始Handler
Object rawHandler = null;
if ("/".equals(lookupPath)) {
rawHandler = getRootHandler();
}
if (rawHandler == null) {
rawHandler = getDefaultHandler();
}
if (rawHandler != null) {
//如果是String类型则到容器中查找具体的bean
if (rawHandler instanceof String) {
String handlerName = (String) rawHandler;
rawHandler = getApplicationContext().getBean(handlerName);
}
// 预留的校验handler模板方法,没有使用
validateHandler(rawHandler, request);
// 添加expose属性到request的拦截器
handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null);
}
}
if (handler != null && logger.isDebugEnabled()) {
logger.debug("Mapping [" + lookupPath + "] to " + handler);
}
else if (handler == null && logger.isTraceEnabled()) {
logger.trace("No handler mapping found for [" + lookupPath + "]");
}
return handler;
}
提示:这里的lookupHandler()方法用于使用lookupPath从Map中查找Handler。buildPathExposiongHandler()方法用于给查找到的Handler注册两个拦截器PathExposingHandlerIntercptor和UriTemplateVariablesHandlerInterceptor。这两个是内部拦截器,主要作用是将与当前url实际匹配的pattern、匹配条件和url模板参数等设置到request的属性中。这样一来在后面的处理过程中就能够直接从request中获取。
protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
// 直接根据url进行查找handler,直接从Map中获取
Object handler = this.handlerMap.get(urlPath);
if (handler != null) {
// 如果是String类型直接从容器中获取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
return buildPathExposingHandler(handler, urlPath, urlPath, null);
}
// Pattern match? 通过表达式进行匹配具体通过AntPathMatcher实现,具体后面分析
List<String> matchingPatterns = new ArrayList<String>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
}
String bestPatternMatch = null;
Comparator<String> patternComparator = getPathMatcher().getPatternComparator(urlPath);
if (!matchingPatterns.isEmpty()) {
Collections.sort(matchingPatterns, patternComparator);
if (logger.isDebugEnabled()) {
logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns);
}
// order序号最小的优先级最高
bestPatternMatch = matchingPatterns.get(0);
}
if (bestPatternMatch != null) {
handler = this.handlerMap.get(bestPatternMatch);
// 如果是String类型则从容器中获取
if (handler instanceof String) {
String handlerName = (String) handler;
handler = getApplicationContext().getBean(handlerName);
}
validateHandler(handler, request);
String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath);
//处理使用sort()方法排序之后,多个Pattern顺序相同,返回值为0的情况
Map<String, String> uriTemplateVariables = new LinkedHashMap<String, String>();
for (String matchingPattern : matchingPatterns) {
if (patternComparator.compare(bestPatternMatch, matchingPattern) == 0) {
Map<String, String> vars = getPathMatcher().extractUriTemplateVariables(matchingPattern, urlPath);
Map<String, String> decodedVars = getUrlPathHelper().decodePathVariables(request, vars);
uriTemplateVariables.putAll(decodedVars);
}
}
if (logger.isDebugEnabled()) {
logger.debug("URI Template variables for request [" + urlPath + "] are " + uriTemplateVariables);
}
return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables);
}
// No handler found...
return null;
}
12.2.1 SimpleUrlHandlerMapping
该类定义了一个Map变量(两个作用:1.方便配置。2.在注册前做一些预处理。如:确保所有的url都以””/“开头),将所有的url和handler的对应关系放在里面,最后注册到父类的Map中。AbstractDetectingUrlHandlerMapping是将容器中所有bean拿出来,按照一定规则注册到父类的Map中。
SimpleUrlHandlerMapping在创建时重写父类的initApplicationContext()方法,调用registerHandlers()方法完成Handler注册。registerHandlers()方法调用AbstractHandlerMapping的registerHandler()方法将配置的urlMap注册到AbstractUrlHandlerMapping的Map中。
总结:该类就是直接将配置的内容注册到AbstractUrlHandlerMapping中去。
12.2.2 AbstractDetectingUrlHandlerMapping
该类也是通过重写initApplicationContext来注册Handler,在里面调用了detectHandler()方法。在detectHandlers()中根据配置的detectHand-lersInAncestorContexts参数从Springmvc容器或者Springmvc及其父容器中找到所有的bean的beanName,然后使用方法determineUrlsHandler()方法对每个beanName解析出对应的urls。
@Override
public void initApplicationContext() throws ApplicationContextException {
//进行初始化
super.initApplicationContext();
detectHandlers();
}
protected void detectHandlers() throws BeansException {
if (logger.isDebugEnabled()) {
logger.debug("Looking for URL mappings in application context: " + getApplicationContext());
}
//获取容器的所有bean的名字
String[] beanNames = (this.detectHandlersInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
getApplicationContext().getBeanNamesForType(Object.class));
// 对每个beanName解析url,如果能解析到就注册到父类的Map中
for (String beanName : beanNames) {
//使用beanName解析url,是模板方法,子类具体实现
String[] urls = determineUrlsForHandler(beanName);
//如果能解析到url则注册到父类
if (!ObjectUtils.isEmpty(urls)) {
// 父类的registerHandler方法
registerHandler(urls, beanName);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Rejected bean name '" + beanName + "': no URL paths identified");
}
}
}
}
//预留的模板方法
protected abstract String[] determineUrlsForHandler(String beanName);
提示:AbstractDetectingUrlHandlerMapping中有三个类:BeanNameUrlHandlerMapping | DefaultAnnotationHandlerMapping(@Deprecated 已被弃用) | AbstractControllerUrlHandlerMapping 。
1.BeanNameUrlHandlerMapping
该类是检查beanName和alias是否是以”/“开头,如果是则将其作为url,里面只有一个determineUrlsForHandler()方法。
/**
* Checks name and aliases of the given bean for URLs, starting with "/".
*/
@Override
protected String[] determineUrlsForHandler(String beanName) {
List<String> urls = new ArrayList<String>();
if (beanName.startsWith("/")) {
urls.add(beanName);
}
String[] aliases = getApplicationContext().getAliases(beanName);
for (String alias : aliases) {
if (alias.startsWith("/")) {
urls.add(alias);
}
}
return StringUtils.toStringArray(urls);
}
2.AbstractControllerUrlHandlerMapping
该类是将实现了Controller接口或注释@Controller的bean作为Handler,同时可以设置excludedClasses和excludedPackages将不包含的bean或者不包含的包下的所有bean排除在外。这里的determineUrlsForHandler()方法主要负责将符合条件的Handler找出来。
//determineUrlsForHandler()方法主要负责将符合条件的Handler找出来
@Override
protected String[] determineUrlsForHandler(String beanName) {
Class beanClass = getApplicationContext().getType(beanName);
if (isEligibleForMapping(beanName, beanClass)) {
return buildUrlsForHandler(beanName, beanClass);
}
else {
return null;
}
}
//判断controller是否被排除在外(通过包package排除或类class排除)
protected boolean isEligibleForMapping(String beanName, Class beanClass) {
if (beanClass == null) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean type could not be determined");
}
return false;
}
//排除excludedClasses里配置的类
if (this.excludedClasses.contains(beanClass)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is explicitly excluded: " + beanClass.getName());
}
return false;
}
String beanClassName = beanClass.getName();
//排除excludedPackages里配置的包下的类
for (String packageName : this.excludedPackages) {
if (beanClassName.startsWith(packageName)) {
if (logger.isDebugEnabled()) {
logger.debug("Excluding controller bean '" + beanName + "' from class name mapping " +
"because its bean class is defined in an excluded package: " + beanClass.getName());
}
return false;
}
}
//检查是否实现了Controller接口或注释了@Controller
return isControllerType(beanClass);
}
2.1 AbstractControllerUrlHandlerMapping 的实现类 ControllerBeanNameUrlHandlerMapping
此类是根据beanName来生产url。
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
List<String> urls = new ArrayList<String>();
urls.add(generatePathMapping(beanName));
String[] aliases = getApplicationContext().getAliases(beanName);// 也获取配置的别名
for (String alias : aliases) {
urls.add(generatePathMapping(alias));
}
return StringUtils.toStringArray(urls);
}
2.2 AbstractControllerUrlHandlerMapping 的实现类 ControllerClassNameUrlHandlerMapping
此类是根据className来生产url。
//对path添加前后缀,还有/
protected String generatePathMapping(String beanName) {
String name = (beanName.startsWith("/") ? beanName : "/" + beanName);
StringBuilder path = new StringBuilder();
if (!name.startsWith(this.urlPrefix)) {
path.append(this.urlPrefix);
}
path.append(name);
if (!name.endsWith(this.urlSuffix)) {
path.append(this.urlSuffix);
}
return path.toString();
}
提示:ControllerClassNameUrlHandlerMapping直接委托给 generatePathMappings 实现。
@Override
protected String[] buildUrlsForHandler(String beanName, Class beanClass) {
return generatePathMappings(beanClass);
}
protected String[] generatePathMappings(Class beanClass) {
StringBuilder pathMapping = buildPathPrefix(beanClass);
String className = ClassUtils.getShortName(beanClass);
String path = (className.endsWith(CONTROLLER_SUFFIX) ?
className.substring(0, className.lastIndexOf(CONTROLLER_SUFFIX)) : className);
if (path.length() > 0) {
if (this.caseSensitive) {
pathMapping.append(path.substring(0, 1).toLowerCase()).append(path.substring(1));
}
else {
pathMapping.append(path.toLowerCase());
}
}
if (isMultiActionControllerType(beanClass)) {
return new String[] {pathMapping.toString(), pathMapping.toString() + "/*"};
}
else {
return new String[] {pathMapping.toString() + "*"};
}
}
private StringBuilder buildPathPrefix(Class beanClass) {
StringBuilder pathMapping = new StringBuilder();
if (this.pathPrefix != null) {
pathMapping.append(this.pathPrefix);
pathMapping.append("/");
}
else {
pathMapping.append("/");
}
if (this.basePackage != null) {
String packageName = ClassUtils.getPackageName(beanClass);
if (packageName.startsWith(this.basePackage)) {
String subPackage = packageName.substring(this.basePackage.length()).replace('.', '/');
pathMapping.append(this.caseSensitive ? subPackage : subPackage.toLowerCase());
pathMapping.append("/");
}
}
return pathMapping;
}
protected boolean isMultiActionControllerType(Class beanClass) {
return this.predicate.isMultiActionControllerType(beanClass);
}
12.3 AbstractHandlerMethodMapping
AbstractHandlerMethodMapping系列只有是三个类:AbstractHandlerMethodMapping | RequestMappingInfoHandlerMapping | RequestMappingHandlerMapping。这一系列是将Method作为Handler来使用的。比如经常使用的@RequestMapping所注释的方法就是这种Handler,它专门有一个类型:HandlerMethod,即Method类型的Handler。
12.3.1 创建AbstractHandlerMethodMapping
该类的作用是定义整个算法流程。这里最重要的理解三个Map的含义:
//该map的作用是保存着匹配条件(RequestCondition)和Handler Method的对应关系。
private final Map<T,HandlerMethod> handlerMethods = new LinkedHashMap<T,HandlerMethod>();
//该map的作用是保存着url与匹配条件的对应关系。
private final MultiValueMap<String,T> urlMap = new LinkedMultiValueMap<String,T>();
//该map的作用是保存着name与handlermathod的对应关系。
private final MultiValueMap<String,HandlerMethod> nameMap = new LinkedMultiValueMap<String,HandlerMethod>();
这里的AbstractHandlerMethodMapping实现了initialziingBean接口,所有Spring容器会自动调用其afterProperties方法和afterPropertiesSet又交给initHandlerMethods()方法完成初始化。
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(obtainApplicationContext(), Object.class) :
obtainApplicationContext().getBeanNamesForType(Object.class));
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
Class<?> beanType = null;
try {
beanType = obtainApplicationContext().getType(beanName);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (beanType != null && isHandler(beanType)) {
detectHandlerMethods(beanName);
}
}
}
handlerMethodsInitialized(getHandlerMethods());
}
提示:1.首先拿到容器里面的所有bean。2.然后根据一定的规则筛选出Handler。3.最后保存到Map中。这里的筛选方法是isHandler()方法,是一个模板方法。具体的筛选是在子类里,筛选的逻辑是检查类前是否有@Controller或者@RequestMapping注解。4.在detectHandlerMethods中,首先从传入的处理器中找到符合要求的方法,然后用registerHandlerMethod进行注册(也就是保存在Map中),从这里可以看出Spring其实是将处理请求的方法所在的类看作处理器,而不是处理请求的方法,不过许多地方需要将请求的方法作为处理器来理解。从handler里获取可以处理请求的method的方法使用。
12.3.1 续 AbstractHandlerMethodMapping 系列之用
这里的主要功能是通过 getHandlerInternal() 方法获取处理器。这里的 getHandlerInternal() 方法做三件事:1.根据request获取ookupPath(url)。2.使用lookupPath和request找handlerMethod。3.如果可以找到handlerMethod则调用它的createWithResolverBean()方法创建新的 HandlerMethod 并返回。这里的 createWithResolverBean() 方法是判断 handlerMethod 里的handler是否是String类型。如果是则改为将其作为 beanName从容器中所取到的bean,但是HandlerMethod中的属性是final类型,不能修改。
12.3.2 RequestMappingInfoHandlerMapping
提供匹配条件RequestMappingInfo的解析处理。
/**
* 获取url集合,即@RequestMapping中设置的value或path
*/
@Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
return info.getPatternsCondition().getPatterns();
}
12.3.3 RequestMappingHandlerMapping
根据@RequestMapping注解生成RequestMappingInfo,同时提供isHandler()方法实现。
/**
* 使用方法和类型注解@RequestMapping创建RequestMappingInfo对象
*/
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 创建方法的RequestMappingInfo
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 创建类的RequestMappingInfo
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 将方法RequestMappingInfo和类RequestMappingInfo合并,比如Controller类上有@RequestMapping("/demo"),方法的@RequestMapping("/demo1"),结果为"/demo/demo1"
info = typeInfo.combine(info);
}
}
return info;
}
@Nullable
private RequestMappingInfo createRequestMappingInfo(AnnotatedElement element) {
// 获取RequestMapping注解
RequestMapping requestMapping = AnnotatedElementUtils.findMergedAnnotation(element, RequestMapping.class);
RequestCondition<?> condition = (element instanceof Class ?
getCustomTypeCondition((Class<?>) element) : getCustomMethodCondition((Method) element));
// 调用createRequestMappingInfo创建匹配条件对象
return (requestMapping != null ? createRequestMappingInfo(requestMapping, condition) : null);
}
/**
* 构造匹配条件对象
*/
protected RequestMappingInfo createRequestMappingInfo(
RequestMapping requestMapping, @Nullable RequestCondition<?> customCondition) {
RequestMappingInfo.Builder builder = RequestMappingInfo
.paths(resolveEmbeddedValuesInPatterns(requestMapping.path()))
.methods(requestMapping.method())
.params(requestMapping.params())
.headers(requestMapping.headers())
.consumes(requestMapping.consumes())
.produces(requestMapping.produces())
.mappingName(requestMapping.name());
if (customCondition != null) {
builder.customCondition(customCondition);
}
return builder.options(this.config).build();
}